#!/usr/bin/env python3

#  Imports.
import tmm.web.base.logger as _logger
import tmm.web.mq.handler as _mq_handler
import tmm.web.mq.messaging as _mq_messaging
import tmm.web.server.defaults as _dflt
import tmm.web.server.static as _static_files
import tmm.web.server.version as _ver
import os as _os
import signal as _signal
import sys as _sys
import threading as _threading

#  Third-party libraries.
import tornado.httpserver as _fm_http
import tornado.ioloop as _fm_ioloop
import tornado.options as _fm_opt
import tornado.web as _fm_web


def interrupt_handler(signal, frame):
    """The handler of SIGINT.

    :param signal: The signal.
    :param frame: The frame.
    """

    #  Check the signal.
    if signal != _signal.SIGINT or frame is None:
        return

    # Show the "Interrupted" message.
    _logger.log_message(
        "Interrupted.",
        level=_logger.LogLevel.ERROR
    )

    #  Exit the program.
    # noinspection PyProtectedMember
    _os._exit(1)


# noinspection PyClassHasNoInit,PyAbstractClass
class IndexHandler(_fm_web.RequestHandler):
    """The index handler."""

    def get(self):
        """Handle the GET request."""

        #  Redirect to the static web page.
        self.redirect("/static/index.html")


def main():
    """The entry of the server."""

    #  Define command-line options.
    _fm_opt.define(
        "server_address",
        default=_dflt.DFLT_SERVER_ADDRESS,
        help="The address of the web server.",
        type=str,
        multiple=False
    )
    _fm_opt.define(
        "server_port",
        default=_dflt.DFLT_SERVER_PORT,
        help="The port number of the web server.",
        type=int,
        multiple=False,
    )

    _fm_opt.define(
        "mq_host",
        default=_dflt.DFLT_MQ_HOST,
        help="The host of the message queue server.",
        type=str,
        multiple=False
    )
    _fm_opt.define(
        "mq_port",
        default=_dflt.DFLT_MQ_PORT,
        help="The port number of the message queue server.",
        type=int,
        multiple=False
    )
    _fm_opt.define(
        "mq_aux_user",
        default=_dflt.DFLT_MQ_AUX_USER,
        help="The user of the authentication credential of the message queue server.",
        type=str,
        multiple=False
    )
    _fm_opt.define(
        "mq_aux_password",
        default=_dflt.DFLT_MQ_AUX_PASSWORD,
        help="The password of the authentication credential of the message queue server.",
        type=str,
        multiple=False
    )
    _fm_opt.define(
        "mq_vhost",
        default=_dflt.DFLT_MQ_VHOST,
        help="The virtual host of the message queue server.",
        type=str,
        multiple=False
    )
    _fm_opt.define(
        "mq_heartbeat",
        default=_dflt.DFLT_MQ_HEARTBEAT,
        help="The heart-beat interval of the message queue server.",
        type=int,
        multiple=False
    )
    _fm_opt.define(
        "mq_reconnect_delay",
        default=_dflt.DFLT_MQ_RECONNECT_DELAY,
        help="The delaying seconds before reconnecting to the message queue server.",
        type=int,
        multiple=False
    )
    _fm_opt.define(
        "mq_tcp_timeout",
        default=_dflt.DFLT_MQ_TCP_TIMEOUT,
        help="The TCP timeout of the connections to the message queue server.",
        type=int,
        multiple=False
    )
    _fm_opt.define(
        "mq_cache_size",
        default=_dflt.DFLT_MQ_CACHE_SIZE,
        help="The client cache size of the message queue server.",
        type=int,
        multiple=False
    )

    #  Parse the options.
    options = _fm_opt.options
    try:
        options.parse_command_line()
    except _fm_opt.Error as error:
        _logger.log_message(
            str(error),
            level=_logger.LogLevel.ERROR
        )
        _sys.exit(1)

    #  Catch the "SIGINT" signal.
    _signal.signal(_signal.SIGINT, interrupt_handler)

    #  Show the banner.
    _logger.log_message(
        "XiaoJSoft TMM Web Server V%d.%d.%d" % (
            _ver.VERSION_MAJOR,
            _ver.VERSION_MINOR,
            _ver.VERSION_REVISION
        ),
        level=_logger.LogLevel.INFORMATION
    )

    #  Check the server port.
    if int(options.server_port) <= 0:
        _logger.log_message(
            "Invalid server port.",
            level=_logger.LogLevel.ERROR
        )
        _sys.exit(1)

    #  Check the MQ port.
    if int(options.mq_port) <= 0:
        _logger.log_message(
            "Invalid server port.",
            level=_logger.LogLevel.ERROR
        )
        _sys.exit(1)

    #  Check the MQ heartbeat.
    if int(options.mq_heartbeat) <= 0:
        _logger.log_message(
            "Invalid heart-beat interval (message queue).",
            level=_logger.LogLevel.ERROR
        )
        _sys.exit(1)

    #  Check the MQ reconnect delay.
    if int(options.mq_reconnect_delay) < 0:
        _logger.log_message(
            "Invalid reconnect delay (message queue).",
            level=_logger.LogLevel.ERROR
        )
        _sys.exit(1)

    #  Check the MQ TCP timeout.
    if int(options.mq_tcp_timeout) <= 0:
        _logger.log_message(
            "Invalid TCP timeout (message queue).",
            level=_logger.LogLevel.ERROR
        )
        _sys.exit(1)

    #  Check the MQ cache size.
    if int(options.mq_cache_size) < _dflt.MIN_MQ_CACHE_SIZE:
        _logger.log_message(
            "Invalid cache size (message queue, least=%d)." % _dflt.MIN_MQ_CACHE_SIZE,
            level=_logger.LogLevel.ERROR
        )
        _sys.exit(1)

    #  Apply the MQ cache size.
    _mq_handler.BUCKET_SIZE = int(options.mq_cache_size)

    #  Create the messaging thread.
    _logger.log_message(
        "Starting the messaging thread...",
        level=_logger.LogLevel.INFORMATION
    )
    msg_thread = _threading.Thread(
        target=_mq_messaging.messaging_thread_entry,
        args=(
            options.mq_host,
            int(options.mq_port),
            options.mq_aux_user,
            options.mq_aux_password,
            options.mq_vhost,
            int(options.mq_heartbeat),
            int(options.mq_reconnect_delay),
            int(options.mq_tcp_timeout),
        )
    )
    msg_thread.start()

    #  Create the web server.
    _logger.log_message(
        "Starting the web server...",
        level=_logger.LogLevel.INFORMATION
    )
    application = _fm_web.Application(
        handlers=[
            (r"/", IndexHandler),
            (r"/static/(.*)", _fm_web.StaticFileHandler, {
                "path": _static_files.STATIC_FILES_DIRECTORY
            }),
            (r"/mq", _mq_handler.MessageQueueHandler)
        ],
        autoreload=False
    )
    server = _fm_http.HTTPServer(application)
    server.listen(
        int(options.server_port),
        address=options.server_address
    )

    #  Start the I/O loop.
    _fm_ioloop.IOLoop.current().start()
